package com.zdzx.service.impl;

import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.zdzx.base.common.util.JwtUtil;
import com.zdzx.base.common.util.RedisUtil;
import com.zdzx.base.common.util.TokenInfo;
import com.zdzx.common.result.Result;
import com.zdzx.common.result.ResultCode;
import com.zdzx.common.result.ResultUtil;
import com.zdzx.common.util.Md5Util;
import com.zdzx.mapper.FunctionMapper;
import com.zdzx.mapper.RoleFunctionMapper;
import com.zdzx.mapper.RoleMapper;
import com.zdzx.mapper.UserMapper;
import com.zdzx.mapper.UserRoleMapper;
import com.zdzx.model.dic.ClientType;
import com.zdzx.model.service.base.param.EditUser;
import com.zdzx.model.service.base.param.LoginInfo;
import com.zdzx.model.service.base.result.UserInfo;
import com.zdzx.model.service.base.vo.Function;
import com.zdzx.model.service.base.vo.Role;
import com.zdzx.model.service.base.vo.RoleFunction;
import com.zdzx.model.service.base.vo.User;
import com.zdzx.model.service.base.vo.UserRole;
import com.zdzx.service.UserService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;

/**
 * <p>
 * 用户实现类
 * </p>
 *
 * @author szl
 * @since 2018-11-08
 */
@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private UserMapper userMapper;
	@Autowired
	private UserRoleMapper userRoleMapper;
	@Autowired
	private RoleFunctionMapper roleFunctionMapper;
	@Autowired
	private FunctionMapper functionMapper;
	@Autowired 
	private RoleMapper roleMapper;
	

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Result<UserInfo> login(LoginInfo loginInfo,HttpServletRequest request) {

		QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
		queryWrapper.eq("account", loginInfo.getAccount());
		List<User> users = userMapper.selectList(queryWrapper);

		Result<UserInfo> validateParam = ValidateParam(loginInfo);
		if (validateParam != null) {
			return validateParam;
		} else if (users == null || users.size() == 0) {
			return ResultUtil.data(ResultCode.USERNAME_ERROR.getCode(), ResultCode.USERNAME_ERROR.getMes(), null);
		} else {
			if(!"0".equals(loginInfo.getLoginType())&&!"1".equals(loginInfo.getLoginType())) {
				loginInfo.setLoginType("0");
			}
			if("0".equals(loginInfo.getLoginType())) {
				if (!users.get(0).getPassword().equals(Md5Util.hash(loginInfo.getPassword()))) {
					return ResultUtil.data(ResultCode.PASSWORD_ERROR.getCode(), ResultCode.PASSWORD_ERROR.getMes(), null);
				} 
			}
			if("1".equals(loginInfo.getLoginType())) {
				//String requestId = request.getHeader("x-request-id");
				String vcode = (String) redisUtil.get("mobileCode:"+loginInfo.getAccount());
				if (StringUtils.isEmpty(vcode) || !vcode.equals(loginInfo.getCode())) {
					return  ResultUtil.data(ResultCode.MOBILE_CODE_ERROR.getCode(), "验证码不正确", null);
				} 
			}
			
			
			User updateTime = new User();
			updateTime.setLastLoginTime(LocalDateTime.now());
			updateTime.setId(users.get(0).getId());
			userMapper.updateById(updateTime);
			
			UserInfo userInfo = new UserInfo();
			if("ADMIN".equals(loginInfo.getFrom())) {
				if("admin".equals(users.get(0).getAccount())) {
					QueryWrapper<Function> functionQueryWrapper=new QueryWrapper<Function>();
					functionQueryWrapper.eq("client_type", loginInfo.getFrom());
					functionQueryWrapper.eq("status", "1");
					functionQueryWrapper.orderByAsc("`index`");
					List<Function> functions = functionMapper.selectList(functionQueryWrapper);
					userInfo.setResouces(functions);
					userInfo.setRole("admin");
				}else {
					QueryWrapper<UserRole> userRoleQueryWrapper=new QueryWrapper<UserRole>();
					userRoleQueryWrapper.eq("user_id", users.get(0).getId());
					List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
					List<String> roleIds=new ArrayList<String>();
					for (UserRole userRole : userRoles) {
						roleIds.add(userRole.getRoleId());
					}
					if(roleIds.size()>0) {
					QueryWrapper<Role> roleQueryWrapper=new QueryWrapper<Role>();
					roleQueryWrapper.in("id", roleIds);
					//roleQueryWrapper.eq("name", "admin");
					List<Role> roleList = roleMapper.selectList(roleQueryWrapper);
					if(roleList!=null&&roleList.size()>0) {
						//userInfo.setRole("admin");
						
						
						QueryWrapper<RoleFunction> roleFunctionQueryWrapper=new QueryWrapper<RoleFunction>();
						roleFunctionQueryWrapper.in("role_id", roleIds);
						List<RoleFunction> roleFunctions = roleFunctionMapper.selectList(roleFunctionQueryWrapper);
						List<String> functionIds=new ArrayList<String>();
						for (RoleFunction roleFunction : roleFunctions) {
							functionIds.add(roleFunction.getFunctionId());
						}
						QueryWrapper<Function> functionQueryWrapper=new QueryWrapper<Function>();
						functionQueryWrapper.eq("client_type", loginInfo.getFrom());
						functionQueryWrapper.eq("status", "1");
						functionQueryWrapper.in("id",functionIds);
						List<Function> functions = functionMapper.selectList(functionQueryWrapper);
						userInfo.setResouces(functions);
					
					}
					}
					
					}
				}
				
		   
			BeanUtils.copyProperties(users.get(0), userInfo);

			if (saveUserInfo(userInfo, loginInfo)) {
				return ResultUtil.data(ResultCode.RESULT_SUCCESS.getCode(), null, userInfo);
			} else {
				//if("WEB".equals(loginInfo.getFrom())) {
					
				//}
				return ResultUtil.data(ResultCode.RESULT_ERROR.getCode(), null, null);
			}
		}

	}

	private Result<UserInfo> ValidateParam(LoginInfo loginInfo) {
		String from = loginInfo.getFrom();
		if (StringUtils.isEmpty(from) || !ClientType.hasKey(from)) {
			return ResultUtil.data(ResultCode.FROM_ERROR.getCode(), ResultCode.FROM_ERROR.getMes(), null);
		}
		return null;
	}

	private boolean saveUserInfo(UserInfo userInfo, LoginInfo loginInfo) {
		String userInfoKey = TokenInfo.getKey(userInfo.getId(), loginInfo.getFrom());
		userInfo.setFrom(loginInfo.getFrom());
		String token = JwtUtil.buildJWT(userInfo.getFrom(), userInfo.getId().toString(), JwtUtil.JWT_DURATION);
		userInfo.setFrom(loginInfo.getFrom());
		userInfo.setToken(token);
		return redisUtil.set(userInfoKey, userInfo, JwtUtil.JWT_DURATION);
	}

	@Override
	public Result<String> refToken(String token) {
		Map<String, Object> parseJWT1 = JwtUtil.parseJWT(JwtUtil.generateKey(JwtUtil.JWT_ALG, JwtUtil.JWT_RULE), token);
		Result<String> result = null;
		if (!(boolean) parseJWT1.get("result")) {
			String code = parseJWT1.get("code").toString();
			ResultCode valueOf = ResultCode.valueOf(code);
			if (valueOf.getCode().equals(ResultCode.TOKEN_EXPIREDJWT.getCode())) {
				try {
					return checkExpriToken(valueOf, token);
				} catch (UnsupportedEncodingException e) {
					return ResultUtil.data(valueOf.getCode(), valueOf.getMes(), null);
				}

			} else {
				result = ResultUtil.data(valueOf.getCode(), valueOf.getMes(), null);
			}
		} else {
			@SuppressWarnings("unchecked")
			Jws<Claims> parseJWT = (Jws<Claims>) parseJWT1.get("code");
			String userId = parseJWT.getBody().getId();
			String subject = parseJWT.getBody().getSubject();
			TokenInfo tokenInfo=new TokenInfo(userId, subject);
			
			boolean hasKey = redisUtil.hasKey(tokenInfo.getKey());
			if (hasKey) {
				UserInfo userInfo = (UserInfo) redisUtil.get(tokenInfo.getKey());
				if (!userInfo.getToken().equals(token)) {
					return ResultUtil.data(ResultCode.TOKEN_SIGNATURE.getCode(), ResultCode.TOKEN_SIGNATURE.getMes(),
							null);
				}
			}
			String refToken = createToken(tokenInfo);
			result = ResultUtil.data(ResultCode.RESULT_SUCCESS.getCode(), ResultCode.RESULT_SUCCESS.getMes(), refToken);
		}
		return result;
	}

	@Override
	public Result<String> logout(String token) {
		Map<String, Object> parseJWT1 = JwtUtil.parseJWT(JwtUtil.generateKey(JwtUtil.JWT_ALG, JwtUtil.JWT_RULE), token);
		Result<String> result = null;
		@SuppressWarnings("unchecked")
		Jws<Claims> parseJWT = (Jws<Claims>) parseJWT1.get("code");
		String userId = parseJWT.getBody().getId();
		String subject = parseJWT.getBody().getSubject();
		String key = TokenInfo.getKey(userId, subject);
		redisUtil.del(key);
		result = ResultUtil.data(ResultCode.RESULT_SUCCESS.getCode(), ResultCode.RESULT_SUCCESS.getMes(), null);

		return result;
	}

	private String createToken(TokenInfo tokenInfo) {
		String token = JwtUtil.buildJWT(tokenInfo.getFrom(), tokenInfo.getId(), JwtUtil.JWT_DURATION);
		User user = userMapper.selectById(tokenInfo.getId());
		UserInfo userInfo = new UserInfo();
		if("ADMIN".equals(tokenInfo.getFrom())) {
			if("admin".equals(user.getAccount())) {
				QueryWrapper<Function> functionQueryWrapper=new QueryWrapper<Function>();
				functionQueryWrapper.eq("client_type", tokenInfo.getFrom());
				functionQueryWrapper.eq("status", "1");
				functionQueryWrapper.orderByAsc("`index`");
				List<Function> functions = functionMapper.selectList(functionQueryWrapper);
				userInfo.setResouces(functions);
				userInfo.setRole("admin");
			}else {
				QueryWrapper<UserRole> userRoleQueryWrapper=new QueryWrapper<UserRole>();
				userRoleQueryWrapper.eq("user_id", user.getId());
				List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
				List<String> roleIds=new ArrayList<String>();
				for (UserRole userRole : userRoles) {
					roleIds.add(userRole.getRoleId());
				}
				if(roleIds.size()>0) {
				QueryWrapper<Role> roleQueryWrapper=new QueryWrapper<Role>();
				roleQueryWrapper.in("id", roleIds);
				//roleQueryWrapper.eq("name", "admin");
				List<Role> roleList = roleMapper.selectList(roleQueryWrapper);
				if(roleList!=null&&roleList.size()>0) {
					//userInfo.setRole("admin");
					
					
					QueryWrapper<RoleFunction> roleFunctionQueryWrapper=new QueryWrapper<RoleFunction>();
					roleFunctionQueryWrapper.in("role_id", roleIds);
					List<RoleFunction> roleFunctions = roleFunctionMapper.selectList(roleFunctionQueryWrapper);
					List<String> functionIds=new ArrayList<String>();
					for (RoleFunction roleFunction : roleFunctions) {
						functionIds.add(roleFunction.getFunctionId());
					}
					QueryWrapper<Function> functionQueryWrapper=new QueryWrapper<Function>();
					functionQueryWrapper.eq("client_type", tokenInfo.getFrom());
					functionQueryWrapper.eq("status", "1");
					functionQueryWrapper.in("id",functionIds);
					List<Function> functions = functionMapper.selectList(functionQueryWrapper);
					userInfo.setResouces(functions);
				
				}
				}
				
				}
			}
		BeanUtils.copyProperties(user, userInfo);
		
		userInfo.setFrom(tokenInfo.getFrom());
		// info.setResouces(null);
		userInfo.setToken(token);
		String key = TokenInfo.getKey(tokenInfo.getId(),tokenInfo.getFrom());
		redisUtil.set(key, userInfo);
		redisUtil.expire(key, JwtUtil.JWT_DURATION);
		return token;
	}

	public Result<String> checkExpriToken(ResultCode code, String token) throws UnsupportedEncodingException {
		TokenInfo info = JwtUtil.parseExpriToken(token);
		if (info != null) {
			boolean hasKey = ClientType.hasKey(info.getFrom() == null ? "test" : info.getFrom());
			if (hasKey && info.getId() != null) {
				String refToken = createToken(info);
				return ResultUtil.data(ResultCode.RESULT_SUCCESS.getCode(), null, refToken);
			} else {
				return ResultUtil.data(code.getCode(), code.getMes(), null);
			}

		} else {
			return ResultUtil.data(code.getCode(), code.getMes(), null);
		}

	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean saveOrUpdate(EditUser entity) {
		int rows = 0;
		try {
			User user = new User();
			BeanUtils.copyProperties(entity, user);
			if (entity.getId() == null) {
				user.setId(IdWorker.getIdStr());
				user.setCreateTime(LocalDateTime.now());
				rows = userMapper.insert(user);
			} else {
				user.setUpdateTime(LocalDateTime.now());
				rows = userMapper.updateById(user);
			}
			if (rows > 0 && entity.getRoles() != null && entity.getRoles().size() > 0) {
				for (String roleId : entity.getRoles()) {
					UserRole uRole = new UserRole();
					uRole.setUserId(user.getId());
					uRole.setRoleId(roleId);
					userRoleMapper.insert(uRole);
				}
				return true;
			} else {
				return rows>0;
			}
		} catch (Exception e) {
			return false;
		}
	}

	public static void main(String[] args) throws UnsupportedEncodingException {
		System.out.println(Md5Util.hash("admin"));
		//System.out.println(Md5Util.hash("admin"));
		/*
		 * String regex = "\\{([^}]*)\\}"; Pattern pattern = Pattern.compile(regex);
		 * Matcher matcher = pattern.matcher(new String((Base64.decodeBase64(
		 * "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJXRUIiLCJqdGkiOiIzMyIsImlhdCI6MTU1NTk4OTc3MSwiZXhwIjoxNTU1OTk2OTcxfQ.HEwRUTnWWsfnlZKbD-Ljqq1uijIOTcSMdmo-mI_3RLk"
		 * )), "UTF-8")); String content = ""; while (matcher.find()) { content =
		 * matcher.group(); } System.out.println(content); System.out.println(new
		 * String((Base64.decodeBase64(
		 * "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJXRUIiLCJqdGkiOiIzMyIsImlhdCI6MTU1NTk4OTc3MSwiZXhwIjoxNTU1OTk2OTcxfQ.HEwRUTnWWsfnlZKbD-Ljqq1uijIOTcSMdmo-mI_3RLk"
		 * )), "UTF-8"));
		 */
		//System.out.println(Md5Util.hash("123456"));
		String p="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJBRE1JTiIsImp0aSI6IjAiLCJpYXQiOjE2MDI1MDA0OTQsImV4cCI6MTYwMjUwNzY5NH0.TgEEfNjiVvhpb7e5RaZsNxPYhfLYpr4eo_VJeuz24lw";
		Map<String, Object> parseJWT = JwtUtil.parseJWT(JwtUtil.generateKey(JwtUtil.JWT_ALG, JwtUtil.JWT_RULE), p);	
		System.out.println(parseJWT);
		
	}

}
